/*
 * $Header: /cvsroot/rjitask/dfish_base_1_2/base_src/com/rongji/dfish/base/crypt/Base64.java,v 1.1 2014/05/03 08:20:33 009 Exp $
 * $Author: 009 $
 * $Revision: 1.1 $
 * $Date: 2014/05/03 08:20:33 $
 *
 * ====================================================================
 *
 * Copyright (C) 2002-2005 by MyVietnam.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * All copyright notices regarding MyVietnam and MyVietnam CoreLib
 * MUST remain intact in the scripts and source code.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Correspondence and Marketing Questions can be sent to:
 * info@MyVietnam.net
 *
 * @author: Minh Nguyen  minhnn@MyVietnam.net
 * @author: Mai  Nguyen  mai.nh@MyVietnam.net
 */
/*
 * $Header: /cvsroot/rjitask/dfish_base_1_2/base_src/com/rongji/dfish/base/crypt/Base64.java,v 1.1 2014/05/03 08:20:33 009 Exp $
 * $Revision: 1.1 $
 * $Date: 2014/05/03 08:20:33 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */

package com.rongji.dfish.base.crypt;


/**
 * This class provides encode/decode for RFC 2045 Base64 as defined by
 * RFC 2045, N. Freed and N. Borenstein.  <a
 * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>:
 * Multipurpose Internet Mail Extensions (MIME) Part One: Format of
 * Internet Message Bodies. Reference 1996
 *
 * @author Jeffrey Rodriguez
 * @version $Id: Base64.java,v 1.1 2014/05/03 08:20:33 009 Exp $
 */
public final class Base64 {
	
  static private final int BASELENGTH = 255;
  static private final int LOOKUPLENGTH = 64;
  static private final int TWENTYFOURBITGROUP = 24;
  static private final int EIGHTBIT = 8;
  static private final int SIXTEENBIT = 16;
//  static private final int SIXBIT = 6;
  static private final int FOURBYTE = 4;
  static private final int SIGN = -128;
  static private final byte PAD = (byte) '=';
  static private byte[] base64Alphabet = new byte[BASELENGTH];
  static private byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
  //static private final Log log = LogSource.getInstance("org.apache.commons.util.Base64");

  static {
    for (int i = 0; i < BASELENGTH; i++) {
      base64Alphabet[i] = -1;
    }
    for (int i = 'Z'; i >= 'A'; i--) {
      base64Alphabet[i] = (byte) (i - 'A');
    }
    for (int i = 'z'; i >= 'a'; i--) {
      base64Alphabet[i] = (byte) (i - 'a' + 26);
    }
    for (int i = '9'; i >= '0'; i--) {
      base64Alphabet[i] = (byte) (i - '0' + 52);
    }

    base64Alphabet['+'] = 62;
    base64Alphabet['/'] = 63;

    for (int i = 0; i <= 25; i++) {
      lookUpBase64Alphabet[i] = (byte) ('A' + i);
    }

    for (int i = 26, j = 0; i <= 51; i++, j++) {
      lookUpBase64Alphabet[i] = (byte) ('a' + j);
    }

    for (int i = 52, j = 0; i <= 61; i++, j++) {
      lookUpBase64Alphabet[i] = (byte) ('0' + j);
    }

    lookUpBase64Alphabet[62] = (byte) '+';
    lookUpBase64Alphabet[63] = (byte) '/';
  }

  public static boolean isBase64(String isValidString) {
    return isArrayByteBase64(isValidString.getBytes());
  }

  public static boolean isBase64(byte octect) {
    //shall we ignore white space? JEFF??
    return (octect == PAD || base64Alphabet[octect] != -1);
  }

  public static boolean isArrayByteBase64(byte[] arrayOctect) {
    int length = arrayOctect.length;
    if (length == 0) {
      // shouldn't a 0 length array be valid base64 data?
      // return false;
      return true;
    }
    for (int i = 0; i < length; i++) {
      if (!Base64.isBase64(arrayOctect[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Encodes hex octects into Base64.
   *
   * @param binaryData Array containing binary data to encode.
   * @return Base64-encoded data.
   */
  public static byte[] encode(byte[] binaryData) {
    int lengthDataBits = binaryData.length * EIGHTBIT;
    int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
    int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
    byte encodedData[] = null;

    if (fewerThan24bits != 0) {
      //data not divisible by 24 bit
      encodedData = new byte[ (numberTriplets + 1) * 4];
    } else {
      // 16 or 8 bit
      encodedData = new byte[numberTriplets * 4];
    }

    byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;

    int encodedIndex = 0;
    int dataIndex = 0;
    int i = 0;
    //log.debug("number of triplets = " + numberTriplets);
    for (i = 0; i < numberTriplets; i++) {
      dataIndex = i * 3;
      b1 = binaryData[dataIndex];
      b2 = binaryData[dataIndex + 1];
      b3 = binaryData[dataIndex + 2];

      //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);

      l = (byte) (b2 & 0x0f);
      k = (byte) (b1 & 0x03);

      encodedIndex = i * 4;
      byte val1 = ( (b1 & SIGN) == 0) ? (byte) (b1 >> 2) :
          (byte) ( (b1) >> 2 ^ 0xc0);
      byte val2 = ( (b2 & SIGN) == 0) ? (byte) (b2 >> 4) :
          (byte) ( (b2) >> 4 ^ 0xf0);
      byte val3 = ( (b3 & SIGN) == 0) ? (byte) (b3 >> 6) :
          (byte) ( (b3) >> 6 ^ 0xfc);

      encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
      //log.debug( "val2 = " + val2 );
      //log.debug( "k4   = " + (k<<4) );
      //log.debug(  "vak  = " + (val2 | (k<<4)) );
      encodedData[encodedIndex + 1] =
          lookUpBase64Alphabet[val2 | (k << 4)];
      encodedData[encodedIndex + 2] =
          lookUpBase64Alphabet[ (l << 2) | val3];
      encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
    }

    // form integral number of 6-bit groups
    dataIndex = i * 3;
    encodedIndex = i * 4;
    if (fewerThan24bits == EIGHTBIT) {
      b1 = binaryData[dataIndex];
      k = (byte) (b1 & 0x03);
      //log.debug("b1=" + b1);
      //log.debug("b1<<2 = " + (b1>>2) );
      byte val1 = ( (b1 & SIGN) == 0) ? (byte) (b1 >> 2) :
          (byte) ( (b1) >> 2 ^ 0xc0);
      encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
      encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
      encodedData[encodedIndex + 2] = PAD;
      encodedData[encodedIndex + 3] = PAD;
    }
    else if (fewerThan24bits == SIXTEENBIT) {

      b1 = binaryData[dataIndex];
      b2 = binaryData[dataIndex + 1];
      l = (byte) (b2 & 0x0f);
      k = (byte) (b1 & 0x03);

      byte val1 = ( (b1 & SIGN) == 0) ? (byte) (b1 >> 2) :
          (byte) ( (b1) >> 2 ^ 0xc0);
      byte val2 = ( (b2 & SIGN) == 0) ? (byte) (b2 >> 4) :
          (byte) ( (b2) >> 4 ^ 0xf0);

      encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
      encodedData[encodedIndex + 1] =
          lookUpBase64Alphabet[val2 | (k << 4)];
      encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
      encodedData[encodedIndex + 3] = PAD;
    }

    return encodedData;
  }

  /**
   * Decodes Base64 data into octects
   *
   * @param base64Data Byte array containing Base64 data
   * @return Array containing decoded data.
   */
  public static byte[] decode(byte[] base64Data) {
    // handle the edge case, so we don't have to worry about it later
    if (base64Data.length == 0) {
      return new byte[0];
    }

    int numberQuadruple = base64Data.length / FOURBYTE;
    byte decodedData[] = null;
    byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;

    // Throw away anything not in base64Data

    int encodedIndex = 0;
    int dataIndex = 0;
    {
      // this sizes the output array properly - rlw
      int lastData = base64Data.length;
      // ignore the '=' padding
      while (base64Data[lastData - 1] == PAD) {
        if (--lastData == 0) {
          return new byte[0];
        }
      }
      decodedData = new byte[lastData - numberQuadruple];
    }

    for (int i = 0; i < numberQuadruple; i++) {
      dataIndex = i * 4;
      marker0 = base64Data[dataIndex + 2];
      marker1 = base64Data[dataIndex + 3];

      b1 = base64Alphabet[base64Data[dataIndex]];
      b2 = base64Alphabet[base64Data[dataIndex + 1]];

      if (marker0 != PAD && marker1 != PAD) {
        //No PAD e.g 3cQl
        b3 = base64Alphabet[marker0];
        b4 = base64Alphabet[marker1];

        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] =
            (byte) ( ( (b2 & 0xf) << 4) | ( (b3 >> 2) & 0xf));
        decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
      }
      else if (marker0 == PAD) {
        //Two PAD e.g. 3c[Pad][Pad]
        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
      }
      else if (marker1 == PAD) {
        //One PAD e.g. 3cQ[Pad]
        b3 = base64Alphabet[marker0];

        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] =
            (byte) ( ( (b2 & 0xf) << 4) | ( (b3 >> 2) & 0xf));
      }
      encodedIndex += 3;
    }
    return decodedData;
  }

}
